home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.mactech.com 2010
/
ftp.mactech.com.tar
/
ftp.mactech.com
/
challenge
/
13.09
/
ChallengeTuring.sit.hqx
/
Challenge, Turing
/
turing.cp
next >
Wrap
Text File
|
1997-06-01
|
8KB
|
278 lines
/*
May 31 1997.
Submission to MacTech Programmer's challenge for June97.
Copyright 1997, Ernst Munter, Kanata, ON, Canada.
"Turing Machine"
Version 2:
A few minor tweaks for a 5% gain in speed.
Problem Statement
-----------------
Implement the engine for a Turing Machine, a state machine
which, at each step, reads a symbol from a tape, consults
a rule which is a function of the current state and the
symbol. The rule specifies a new state, a symbol to output,
and the direction in which to move the tape, or to halt.
Solution
--------
I first try to build a lookup table as an index into the
rules array. But this may require an "unreasonable"
amount of memory.
First, I scan the rules to determine the amount of table
memory required for a simple lookup index. If this
appears to be too much, I go to plan B: a hashed index.
The hash table uses linear open addressing: when the table
is built and an index location is needed which is already
in use we have a collision. To resolve it, I scan linearly
through the index array until a free location is found.
The size of the index array is larger than the number of
rules, so a free location will always be found.
Optimization of hash table lookup
---------------------------------
The majority of rules will hash to unique index addresses.
Rules which hash to the same value can be seen as a
sequence of index table entries, with the primary location
containing the first rule address to be found.
When the Turing Machine is executing, any colliding rule
that is encountered will have its index moved to the
primary index location, on the assumption that it will be
used again, and will then be found more quickly.
Assumptions
-----------
A minimum amount of table memory of 8K entries is always
provided. But for larger rule sets, an index table that
will occupy about 1/2 to 3/4 the amount of memory taken
by the rules array itself may be allocated.
The memory allocated for the simple lookup table will be
the number_of_states * number_of_symbols, rounded up to
a power of 2, but not more than 8K entries,
or 2 * number_of_rules, whichever is larger.
If the memory required for the simple index would exceed
those rules, for example if there are a lot of holes
in the symbol/state space, the hashed index is used.
The memory allocated for the hash index array will
be the larger of 8K entries or 2 * number_of_rules, rounded
up to the nearest power of 2.
For example, a rule set of up to 2K rules may result in
a 32K byte index; a rule set of 50,000 rules which occupy
1M of rules memory may get an index table of 512K bytes.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "turing.h"
const enum { // constants controlling min size of index
EXPANSION = 2,
MIN_BITS = 13,
MIN_SIZE = 1L<<MIN_BITS };
typedef const TMRule* TMRulePtr;
Boolean TuringMachine(
const TMRule theRules[],
ulong numRules,
ulong *theTape,
ulong tapeLen,
long rwHeadPos,
TMMoveProc ReportMove
) {
TMRulePtr* index;
ulong state=0;
ulong symbol;
int direction;
ulong* tape=theTape+rwHeadPos;
ulong* tapeEnd=theTape+tapeLen;
ulong mask;
TMRulePtr rule=theRules;
// The function contains 2 very similar sections,
// one section uses a plain lookup table for an index,
// the other uses a hash table.
// Try to construct a collision-free index of rule addresses
// compute table size
ulong maxState=rule->oldState;
ulong maxSym=rule->inputSymbol;
ulong minSym=rule->inputSymbol;
rule++;
for (int i=1;i<numRules;i++) {
if (maxState<rule->oldState) maxState=rule->oldState;
if (maxSym<rule->inputSymbol) maxSym=rule->inputSymbol;
else
if (minSym>rule->inputSymbol) minSym=rule->inputSymbol;
rule++;
}
ulong numSyms=maxSym-minSym+1;
ulong numStates=maxState+1;
ulong numIndex=(numStates)*(numSyms);
if ((numIndex<numStates) // overflow
|| ((numIndex > MIN_SIZE)
&& (numIndex > EXPANSION*numRules)))// too large
goto try_hash;
// increase size to the next power of 2
ulong dummy=1;
while (numIndex) {
numIndex>>=1;
dummy<<=1;
}
numIndex=dummy;
// Allocate the table memory
index=(TMRulePtr*)malloc(numIndex*sizeof(TMRulePtr));
// Always expect to get the memory, but just in case ...
if (index==0)
return FALSE;
// All unused index locations will remain 0
memset(index,0,numIndex*sizeof(TMRulePtr));
mask=numIndex-1;
// Scan the rules and populate the index array
rule=theRules;
for (int i=0;i<numRules;i++) {
ulong addr=mask & (rule->oldState*numSyms+rule->inputSymbol);
index[addr]=rule++;
}
// Using the collision-free index table:
// Loop until the tape halts or we fail on error
do {
symbol=*tape;
ulong addr=mask & (state*numSyms+symbol);
rule=index[addr];
if (rule == 0) // illegal symbol, no rule
break;
symbol=rule->outputSymbol;
state=rule->newState;
direction=rule->moveDirection;
ReportMove(symbol,state,MoveDir(direction));
*tape=symbol;
if (direction==kHalt) {
free(index);
return TRUE;
}
tape+=direction;
} while ((tape>=theTape) && (tape<tapeEnd));
free(index);
return FALSE;
try_hash:
// Section 2
// If we get here, we could not make a simple table
// and have to go with a hash table, collisions are possible
// Find table size >= minimum size
numIndex=MIN_SIZE;
while (numIndex < EXPANSION*numRules) {
numIndex*=2;
}
// Allocate the table memory
index=(TMRulePtr*)malloc(numIndex*sizeof(TMRulePtr));
// Always expect to get the memory, but just in case ...
if (index==0)
return FALSE;
// All unused index locations will remain 0
memset(index,0,numIndex*sizeof(TMRulePtr));
mask=numIndex-1;
ulong hFactor=1 | (numIndex/numStates);
long hDelta=1 | (hFactor>>1);
// Scan the rules and populate the index array
rule=theRules;
for (int i=0;i<numRules;i++) {
ulong addr=mask & (rule->oldState*hFactor+rule->inputSymbol);
// if primary location is not empty: find next free location
while (index[addr])
addr=mask & (addr+hDelta);
index[addr]=rule++;
}
// Using the hash index table:
// Loop until the tape halts or we fail on error.
// This loop is the same as the loop in the first section
// except we have to check for possible collisions with each rule..
do {
symbol=*tape;
ulong addr=mask & (state*hFactor+symbol);
rule=index[addr];
if (rule == 0) // illegal symbol, no rule
break;
// check if we have the right rule, or a collision
if ((symbol != rule->inputSymbol)
|| (state != rule->oldState)) {
const TMRule* rule0=rule;
ulong addr0=addr;
do { // resolve the collision
addr=mask & (addr+hDelta);
rule=index[addr];
if (rule == 0) { // could not find rule
free(index);
return FALSE;
}
} while ((symbol != rule->inputSymbol)
|| (state != rule->oldState));
index[addr]=rule0; // move last-used rule
index[addr0]=rule; // up in chain
}
// now we have the correct rule
symbol=rule->outputSymbol;
state=rule->newState;
direction=rule->moveDirection;
ReportMove(symbol,state,MoveDir(direction));
*tape=symbol;
if (direction==kHalt) { // normal stop
free(index);
return TRUE;
}
tape+=direction;
} while ((tape>=theTape) && (tape<tapeEnd));
free(index);
return FALSE;
}